grammar Html;

options {
  language = Java;
}

@header {
  import javax.swing.text.html.HTMLEditorKit;
  import javax.swing.text.MutableAttributeSet;
  import javax.swing.text.SimpleAttributeSet;
  import javax.swing.text.html.HTML;
}
  
@members {
  public HTMLEditorKit.ParserCallback cb;
  public MutableAttributeSet attrSet = new SimpleAttributeSet();
  public MyParserDelegator pd;
}

a_tag:
  '<a' attribute * '>' {cb.handleStartTag(pd.strToTag("a"),attrSet,0);attrSet.removeAttributes(attrSet);}
   a_content + 
   '</a>' {cb.handleEndTag(pd.strToTag("a"),0);}
;

a_content:
  heading
  |text
;

address_tag:
  '<address' attribute * '>' {cb.handleStartTag(pd.strToTag("address"),attrSet,0);attrSet.removeAttributes(attrSet);}
   address_content * 
   '</address>' {cb.handleEndTag(pd.strToTag("address"),0);}
;

address_content:
  p_tag
  |text
;

applet_content:
  '<param' attribute * '>'* body_content
;

applet_tag:
  '<applet' attribute * '>' applet_content '</applet>'
;

attribute:
  ATTRIBUTE '=' '"' plain_text? ' '* '"' ' '*  {attrSet.addAttribute(pd.strToAttr($ATTRIBUTE.text),($plain_text.text!=null ? $plain_text.text : " "));}
  |ATTRIBUTE '=' plain_text {attrSet.addAttribute(pd.strToAttr($ATTRIBUTE.text),$plain_text.text);}
;

b_tag:
  '<b' attribute * '>' {cb.handleStartTag(pd.strToTag("b"),attrSet,0);attrSet.removeAttributes(attrSet);}
   text 
   '</b>' {cb.handleEndTag(pd.strToTag("b"),0);}
;

basefont_tag:
  '<basefont' attribute * '>' body_content '</basefont>'
;

big_tag:
  '<big' attribute * '>' text'</big>'
;

block:
  '<isindex' attribute * '>'
  |basefont_tag
  |blockquote_tag
  |center_tag
  |dir_tag
  |div_tag
  |dl_tag
  |form_tag
  |menu_tag
  |ol_tag
  |p_tag
  |pre_tag
  |table_tag
  |ul_tag
;

blockquote_tag:
  '<blockquote' attribute * '>' body_content '</blockquote>'
;

body_content:
  '<bgsound' attribute * '>'
  |'<hr' attribute * '>'
    {
      cb.handleStartTag(pd.strToTag("hr"),attrSet,0);
      attrSet.removeAttributes(attrSet);
      cb.handleEndTag(pd.strToTag("hr"),0);
    }
  |address_tag
  |block
  |comment_tag
  |heading
  |map_tag
  |script_tag
  |text
;

body_tag:
  '<body' attribute * '>' {cb.handleStartTag(pd.strToTag("body"),attrSet,0);attrSet.removeAttributes(attrSet);}
  body_content * 
  '</body>' {cb.handleEndTag(pd.strToTag("body"),0);}
;

caption_tag:
  '<caption' attribute * '>' body_content '</caption>'
;

center_tag:
  '<center' attribute * '>' body_content '</center>'
;

cite_tag:
  '<cite' attribute * '>' text '</cite>'
;

code_tag:
  '<code' attribute * '>' text '</code>'
;

comment_tag:
  '<!--' .* '-->'
;

colgroup_content:
  ('<col' attribute * '>')*
;

colgroup_tag:
  '<colgroup' attribute * '>' colgroup_content
;

content_style:
  cite_tag
  |code_tag
  |dfn_tag
  |em_tag
  |kbd_tag
  |samp_tag
  |strong_tag
  |var_tag
;

dd_tag:
  '<dd' attribute * '>' flow '</dd>'
;

dfn_tag:
  '<dfn' attribute * '>' text '</dfn>'
;

dir_tag:
  '<dir' attribute * '>' {cb.handleStartTag(pd.strToTag("dir"),attrSet,0);attrSet.removeAttributes(attrSet);}
  li_tag + 
  '</dir>' {cb.handleEndTag(pd.strToTag("dir"),0);}
;

div_tag:
  '<div' attribute * '>' {cb.handleStartTag(pd.strToTag("div"),attrSet,0);attrSet.removeAttributes(attrSet);}
   body_content 
   '</div>' {cb.handleEndTag(pd.strToTag("div"),0);}
;

dl_content:
  dt_tag dd_tag
;

dl_tag:
  '<dl' attribute * '>' {cb.handleStartTag(pd.strToTag("dl"),attrSet,0);attrSet.removeAttributes(attrSet);}
  dl_content + 
  '</dl>' {cb.handleEndTag(pd.strToTag("dl"),0);}
;

doctype:
  '<!doctype' .* '>' // HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">'
;

dt_tag:
  '<dt' attribute * '>' {cb.handleStartTag(pd.strToTag("dt"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</dt>' {cb.handleEndTag(pd.strToTag("dt"),0);}
;

em_tag:
  '<em' attribute * '>' {cb.handleStartTag(pd.strToTag("em"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</em>' {cb.handleEndTag(pd.strToTag("em"),0);}
;

flow:
  block
  |text
;

font_tag:
  '<font' attribute * '>' {cb.handleStartTag(pd.strToTag("font"),attrSet,0);attrSet.removeAttributes(attrSet);}
  plain_text 
  '</font>' {cb.handleEndTag(pd.strToTag("font"),0);}
;

form_content:
  '<input' attribute * '>'
  |'<keygen' attribute * '>'
  |body_content
  |select_tag
  |textarea_tag
;

form_tag:
  '<form' attribute * '>' {cb.handleStartTag(pd.strToTag("form"),attrSet,0);attrSet.removeAttributes(attrSet);}
  form_content * 
  '</form>' {cb.handleEndTag(pd.strToTag("form"),0);}
;

frameset_content:
  '<frame' attribute * '>'
  |noframes_tag
;

frameset_tag:
  '<frameset' attribute * '>' frameset_content * '</frameset>'
;

h1_tag:
  '<h1' attribute * '>' {cb.handleStartTag(pd.strToTag("h1"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</h1>' {cb.handleEndTag(pd.strToTag("h1"),0);}
;

h2_tag:
  '<h2' attribute * '>' {cb.handleStartTag(pd.strToTag("h2"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</h2>' {cb.handleEndTag(pd.strToTag("h2"),0);}
;

h3_tag:
  '<h3' attribute * '>' {cb.handleStartTag(pd.strToTag("h3"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</h3>' {cb.handleEndTag(pd.strToTag("h3"),0);}
;

h4_tag:
  '<h4' attribute * '>' {cb.handleStartTag(pd.strToTag("h4"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</h4>' {cb.handleEndTag(pd.strToTag("h4"),0);}
;

h5_tag:
  '<h5' attribute * '>' {cb.handleStartTag(pd.strToTag("h5"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</h5>' {cb.handleEndTag(pd.strToTag("h5"),0);}
;

h6_tag:
  '<h6' attribute * '>' {cb.handleStartTag(pd.strToTag("h6"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</h6>' {cb.handleEndTag(pd.strToTag("h6"),0);}
;

head_content:
  '<base' attribute * ' '* '>'
  |'<base' attribute * ' '* '/>'
  |'<isindex' attribute * '>'
  |'<link' attribute * ' '* '>'
  |'<link' attribute * ' '* '/>'
  |'<meta' attribute * ' '*  '>'
  |'<meta' attribute * ' '* '/>'
  |'<nextid' attribute * '>'
  |script_tag
  |style_tag
  |title_tag
;

head_tag:
  '<head' attribute * '>' {cb.handleStartTag(pd.strToTag("head"),attrSet,0);attrSet.removeAttributes(attrSet);}
  head_content * 
  '</head>' {cb.handleEndTag(pd.strToTag("head"),0);}
;

heading:
  h1_tag
  |h2_tag
  |h3_tag
  |h4_tag
  |h5_tag
  |h6_tag
;

html_content:
  head_tag body_tag
  |head_tag frameset_tag
;

html_document:
  doctype ? html_tag
;

html_tag:
  '<html' attribute * '>' {cb.handleStartTag(pd.strToTag("html"),attrSet,0);attrSet.removeAttributes(attrSet);}
  html_content 
  '</html>' {cb.handleEndTag(pd.strToTag("html"),0);}
;

i_tag:
  '<i' attribute * '>' {cb.handleStartTag(pd.strToTag("i"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</i>' {cb.handleEndTag(pd.strToTag("i"),0);}
;

kbd_tag:
  '<kbd' attribute * '>' text '</kbd>'
;

li_tag:
  '<li' attribute * '>' {cb.handleStartTag(pd.strToTag("li"),attrSet,0);attrSet.removeAttributes(attrSet);}
  flow 
  '</li>' {cb.handleEndTag(pd.strToTag("li"),0);}
;

map_content:
  ('<area' attribute * '>')*
;

map_tag:
  '<map' attribute * '>' map_content '</map>'
;

menu_tag:
  '<menu' attribute * '>' li_tag * '</menu>'
;

noframes_tag:
  '<noframes' attribute * '>' body_content * '</noframes>'
;

object_content:
  ('<param' attribute * '>')* body_content
;

object_tag:
  '<object' attribute * '>' object_content '</object>'
;

ol_tag:
  '<ol' attribute * '>' {cb.handleStartTag(pd.strToTag("ol"),attrSet,0);attrSet.removeAttributes(attrSet);}
  li_tag + 
  '</ol>' {cb.handleEndTag(pd.strToTag("ol"),0);}
;

option_tag:
  '<option' attribute * '>' {cb.handleStartTag(pd.strToTag("option"),attrSet,0);attrSet.removeAttributes(attrSet);}
  plain_text 
  '</option>' {cb.handleEndTag(pd.strToTag("option"),0);}
;

p_tag:
  '<p' attribute * '>' {cb.handleStartTag(pd.strToTag("p"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</p>' {cb.handleEndTag(pd.strToTag("p"),0);}
;

physical_style:
  b_tag
  |big_tag
  |font_tag
  |i_tag
  |s_tag
  |small_tag
  |span_tag
  |strike_tag
  |sub_tag
  |sup_tag
  |tt_tag
  |u_tag
;

pre_content:
  '<br' attribute * '>'
    {
      cb.handleStartTag(pd.strToTag("br"),attrSet,0);
      attrSet.removeAttributes(attrSet);
      cb.handleEndTag(pd.strToTag("br"),0);
    }
  |'<hr' attribute * '>'
    {
      cb.handleStartTag(pd.strToTag("hr"),attrSet,0);
      attrSet.removeAttributes(attrSet);
      cb.handleEndTag(pd.strToTag("hr"),0);
    }
  |'<img' attribute * ' '* '>'
    {
      cb.handleStartTag(pd.strToTag("img"),attrSet,0);
      attrSet.removeAttributes(attrSet);
      cb.handleEndTag(pd.strToTag("img"),0);
    }
  |'<img' attribute * ' '* '/>' 
    {
      cb.handleStartTag(pd.strToTag("img"),attrSet,0);
      attrSet.removeAttributes(attrSet);
      cb.handleEndTag(pd.strToTag("img"),0);
    }
  |a_tag
  |plain_text
;

pre_tag:
  '<pre' attribute * '>'  {cb.handleStartTag(pd.strToTag("pre"),attrSet,0);attrSet.removeAttributes(attrSet);}
  pre_content * 
  '</pre>' {cb.handleEndTag(pd.strToTag("pre"),0);}
;

s_tag:
  '<s' attribute * '>' text '</s>'
;

samp_tag:
  '<samp' attribute * '>' text '</samp>'
;

script_tag:
  '<script' attribute * ' '* '>' {cb.handleStartTag(pd.strToTag("script"),attrSet,0);attrSet.removeAttributes(attrSet);}
  js_text *
  '</script>' {cb.handleEndTag(pd.strToTag("script"),0);}
;

select_tag:
  '<select' attribute * '>' {cb.handleStartTag(pd.strToTag("select"),attrSet,0);attrSet.removeAttributes(attrSet);}
  option_tag + 
  '</select>' {cb.handleEndTag(pd.strToTag("select"),0);}
;

small_tag:
  '<small' attribute * '>' text '</small>'
;

span_tag:
  '<span' attribute * '>' {cb.handleStartTag(pd.strToTag("span"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</span>' {cb.handleEndTag(pd.strToTag("span"),0);}
;

strike_tag:
  '<strike' attribute * '>' text '</strike>'
;

strong_tag:
  '<strong' attribute * '>' text '</strong>'
;

style_tag:
  '<style' attribute * '>' plain_text '</style>'
;

sub_tag:
  '<sub' attribute * '>' text '</sub>'
;

sup_tag:
  '<sup' attribute * '>' text '</sup>'
;

table_cell:
  td_tag
  |th_tag
;

table_content:
  '<tbody' attribute * '>'
  |'<tfoot' attribute * '>'
  |'<thead' attribute * '>'
  |tr_tag
;

table_tag:
  '<table' attribute * '>' {cb.handleStartTag(pd.strToTag("table"),attrSet,0);attrSet.removeAttributes(attrSet);}
  caption_tag ? colgroup_tag * table_content * 
  '</table>' {cb.handleEndTag(pd.strToTag("table"),0);}
;

td_tag:
  '<td' attribute * '>' {cb.handleStartTag(pd.strToTag("td"),attrSet,0);attrSet.removeAttributes(attrSet);}
  body_content 
  '</td>' {cb.handleEndTag(pd.strToTag("td"),0);}
;

text:
  '<br' attribute * '>'
    {
      cb.handleStartTag(pd.strToTag("br"),attrSet,0);
      attrSet.removeAttributes(attrSet);
      cb.handleEndTag(pd.strToTag("br"),0);
    }
  |'<embed' attribute * '>'
  |'<iframe' attribute * '>'
  |'<img' attribute * ' '* '>'
    {
      cb.handleStartTag(pd.strToTag("img"),attrSet,0);
      attrSet.removeAttributes(attrSet);
      cb.handleEndTag(pd.strToTag("img"),0);
    }
  |'<img' attribute * ' '* '/>' 
    {
      cb.handleStartTag(pd.strToTag("img"),attrSet,0);
      attrSet.removeAttributes(attrSet);
      cb.handleEndTag(pd.strToTag("img"),0);
    }
  |'<spacer' attribute * '>'
  |'<wbr' attribute * '>'
  |a_tag
  |applet_tag
  |content_style
  |object_tag
  |displayed_text
  |physical_style
;

textarea_tag:
  '<textarea' attribute * '>' {cb.handleStartTag(pd.strToTag("textarea"),attrSet,0);attrSet.removeAttributes(attrSet);}
  plain_text '</textarea>'
;

th_tag:
  '<th' attribute * '>' {cb.handleStartTag(pd.strToTag("th"),attrSet,0);attrSet.removeAttributes(attrSet);}
  body_content 
  '</th>' {cb.handleEndTag(pd.strToTag("th"),0);}
;

title_tag:
  '<title' attribute * '>' {cb.handleStartTag(pd.strToTag("title"),attrSet,0);attrSet.removeAttributes(attrSet);}
  plain_text 
  '</title>' {cb.handleEndTag(pd.strToTag("title"),0);}
;

tr_tag:
  '<tr' attribute * '>' {cb.handleStartTag(pd.strToTag("tr"),attrSet,0);attrSet.removeAttributes(attrSet);}
  table_cell * 
  '</tr>' {cb.handleEndTag(pd.strToTag("tr"),0);}
;

tt_tag:
  '<tt' attribute * '>' {cb.handleStartTag(pd.strToTag("tt"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</tt>' {cb.handleEndTag(pd.strToTag("tt"),0);}
;

u_tag:
  '<u' attribute * '>' {cb.handleStartTag(pd.strToTag("u"),attrSet,0);attrSet.removeAttributes(attrSet);}
  text 
  '</u>' {cb.handleEndTag(pd.strToTag("u"),0);}
;

ul_tag:
  '<ul' attribute * '>' {cb.handleStartTag(pd.strToTag("ul"),attrSet,0);attrSet.removeAttributes(attrSet);}
  li_tag + 
  '</ul>' {cb.handleEndTag(pd.strToTag("ul"),0);}
;

var_tag:
  '<var' attribute * '>' text '</var>'
;

plain_text:
  plain_text_content
;

plain_text_content:
  (WORD|'/'|'.'|'['|']'|'='|'?'|'-'|' '|'&'|';'|':'|'~'|'('|')'|'_'|ATTRIBUTE) +
;

js_text:
  (WORD|'/'|'.'|'['|']'|'='|'?'|'-'|' '|';'|':'|'~'|'('|')'|'_'|ATTRIBUTE) +
;

displayed_text:
  displayed_text_content {cb.handleText($displayed_text_content.text.toCharArray(),0);}
;

displayed_text_content:
  (WORD|'/'|'.'|'['|']'|'='|'?'|'-'|' '|'&'|';'|':'|'~'|'('|')'|'_'|ATTRIBUTE) +
;

ATTRIBUTE : 
  ' '?
  ( 'align'  
  | 'alt'
  | 'cellpadding'
  | 'cellspacing'
  | 'colspan'
  | 'content'
  | 'href' 
  | 'http-equiv'
  | 'id'
  | 'language'
  | 'media'
  | 'name'
  | 'rel' 
  | 'style' 
  | 'src'
  | 'title'
  | 'type'
  | 'valign' )
;

WORD : 
  ('a'..'z'|'A'..'Z'|'0'..'9')+ 
;

WS :
  (' '|'\n'|'\r'|'\t')+ {$channel=HIDDEN;}
;

